package com.sdy.dcsb.web.controller.inboundservice.util;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.sdy.dcsb.biz.constants.MqConstants;
import com.sdy.dcsb.biz.model.*;
import com.sdy.dcsb.biz.service.*;
import com.sdy.mq.service.MqProducerService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.httpclient.NameValuePair;
import org.apache.commons.httpclient.methods.multipart.Part;
import org.apache.commons.httpclient.methods.multipart.StringPart;
import org.apache.commons.lang3.StringEscapeUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.SocketTimeoutException;
import java.net.URL;
import java.sql.Timestamp;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static org.springframework.util.StringUtils.isEmpty;

/**
 * <p>
 * 原子服务请求处理器
 * </p>
 *
 * @author fb
 * @since 2019/10/31
 */
@Slf4j
@Component
public class AtomProcessorUtil {

    @Autowired
    private EsbAtomServiceService esbAtomServiceService;
    @Autowired
    private EsbAdapderHttpService esbAdapderHttpService;
    @Autowired
    private EsbAdapderSoapService esbAdapderSoapService;
    @Autowired
    private EsbAppService esbAppService;
    @Autowired
    private EsbServiceAtomDeployService esbServiceAtomDeployService;
    @Autowired
    private MqProducerService mqProducerService;

    private String atomReqStatus = null;

    /**
     * 处理原子服务请求
     * @param atomServiceCode
     * @param requestHttpType
     * @param queryString
     * @param contentType
     * @param requestMsg
     * @param reqCharset
     * @param reqDcsbId
     * @param reqType 1:WEBSERVICE,2:HTTP,3:H5
     * @return
     */
    public String doProcessAtom(String atomServiceCode, String requestHttpType, String queryString, String contentType,
                                String requestMsg, String reqCharset, String reqDcsbId, String reqType, String ipAndPort,
                                HashMap<String, String> map) {

        long atomRequestTime = System.currentTimeMillis();
        long atomResponseTime = 1L;
        String responseMsg = "DCSB";
        Integer atomServiceId = -1;
        // 设置默认值
        Integer atomOverTime = 30000;
        List<EsbAtomservice> esbAtomserviceResult = esbAtomServiceService.selectAtomServiceByCode(atomServiceCode);
        String appNmae = "subapp";
        String domainName = "Domain";
        try{
            if(esbAtomserviceResult.size() == 0) {
                setAtomReqStatus("2");
                responseMsg = "This service code("+ atomServiceCode +") doesn't have a corresponding atomservice.";
                throw new Exception(responseMsg);
            }
            // 获取原子服务的id和是否在用状态
            atomServiceId = esbAtomserviceResult.get(0).getId();
            String serIp = ipAndPort.substring(0, ipAndPort.indexOf(":"));
            String serPort = ipAndPort.substring(ipAndPort.indexOf(":")+1);
            if("localhost".equals(serIp)) {
                serIp = LocalhostUtil.getIpAddress();
            }
            QueryWrapper<EsbApp> esbAppWrapper = new QueryWrapper();
            esbAppWrapper.eq("ip", serIp);
            esbAppWrapper.eq("httpport", serPort);
            esbAppWrapper.eq("apptype", "5");
            List<EsbApp> esbAppResult = esbAppService.list(esbAppWrapper);
            if(esbAppResult.size() == 0) {
                setAtomReqStatus("3");
                responseMsg = "原子服务请求失败，未知的请求服务IP: " + serIp + " 和端口: " + serPort;
                throw new Exception(responseMsg);
            }
            appNmae = esbAppResult.get(0).getAppname();
            domainName = esbAppResult.get(0).getDomainname();
            if("0".equals(esbAppResult.get(0).getStatus())) {
                setAtomReqStatus("3");
                responseMsg = "原子服务所在的服务器不可用!";
                throw new Exception(responseMsg);
            }
            // 通过atomserviceid查询原子服务部署信息
            QueryWrapper<EsbServiceAtomDeploy> esbServiceAtomDeployWrapper = new QueryWrapper();
            esbServiceAtomDeployWrapper.eq("serviceid", atomServiceId);
            esbServiceAtomDeployWrapper.eq("instancename", appNmae);
            esbServiceAtomDeployWrapper.eq("deploytype", 1);
            List<EsbServiceAtomDeploy> esbServiceAtomDeployResult = esbServiceAtomDeployService.list(esbServiceAtomDeployWrapper);
            if(esbServiceAtomDeployResult.size() == 0 || "0".equals(esbServiceAtomDeployResult.get(0).getStatus())) {
                setAtomReqStatus("3");
                responseMsg = "未配置原子服务部署信息";
                throw new Exception(responseMsg);
            }
            if(!"1".equals(esbAtomserviceResult.get(0).getStatus())) {
                setAtomReqStatus("2");
                responseMsg = "This atomic service ("+ atomServiceCode +") has been logged out and cannot be called.";
                throw new Exception(responseMsg);
            }
            // 获取请求的适配类型、请求头类型、超时时间
            String adapterType = esbAtomserviceResult.get(0).getAdapterType();
            String atomContentType = esbAtomserviceResult.get(0).getContentType();
            String overTime = esbAtomserviceResult.get(0).getOverTime();
            atomOverTime = Integer.valueOf(overTime);
            // 适配类型转中文
            String adapterTypeName = null;
            if("1".equals(adapterType)) {
                adapterTypeName = "webservice";
            }else if("2".equals(adapterType)) {
                adapterTypeName = "http";
            }else {
                adapterTypeName = "existence";
            }
            // 先判断原子服务的适配类型是否是HTTP，再判断是否为SOAP
            if("1".equals(adapterType) && "2".equals(reqType)) {
                //log.info("HTTP");
            }else if("2".equals(adapterType) && "1".equals(reqType)) {
                //log.info("SOAP");
            }else {
                setAtomReqStatus("3");
                responseMsg = "The adaptation type of this atomservice is not " + adapterTypeName +
                        "! Please check the service configuration.";
                throw new Exception(responseMsg);
            }
            EsbAdapderHttp esbAdapderHttpResult = esbAdapderHttpService.getById(atomServiceId);
            EsbAdapderSoap esbAdapderSoapResult = esbAdapderSoapService.getById(atomServiceId);
            // 获取原子服务请求的发送字符集、返回字符集、原始接口地址
            String atomReqCharset = null;
            String atomRespCharset = null;
            String atomUrl = null;
            String httpType = "";
            String postChangedGet = "";
            if(!isEmpty(esbAdapderHttpResult) && "2".equals(reqType)) {
                atomReqCharset = esbAdapderHttpResult.getScharset();
                atomRespCharset = esbAdapderHttpResult.getRcharset();
                atomUrl = esbAdapderHttpResult.getServiceUrl();
                if("1".equals(esbAdapderHttpResult.getHttptype())) {
                    httpType = "GET";
                }else {
                    httpType = "POST";
                }
                // 确定用户访问的方式和原子服务配置的方式一样
                if(httpType.equalsIgnoreCase(requestHttpType)) {
                    // 跳出if判断继续往下执行
                }else {
                    postChangedGet = "1";
                }
            }else if(!isEmpty(esbAdapderSoapResult) && "1".equals(reqType)) {
                atomReqCharset = esbAdapderSoapResult.getScharset();
                atomRespCharset = esbAdapderSoapResult.getRcharset();
                atomUrl = esbAdapderSoapResult.getServiceUrl();
                // 跳出if判断继续往下执行
            }else {

                setAtomReqStatus("3");
                responseMsg = "The atomservice ("+ atomServiceCode +") doesn't have a corresponding request method!" +
                        "Please revisit after configuration.";
                throw new Exception(responseMsg);
            }

            if(!"http".equals(atomUrl.substring(0, 4))) {
                setAtomReqStatus("3");
                responseMsg = "Please check the url of the service configuration ,use correct url begining with http or https.";
                throw new Exception(responseMsg);
            }
            if(isEmpty(atomReqCharset)) {
                atomReqCharset = "UTF-8";
            }
            if(isEmpty(atomRespCharset)) {
                atomRespCharset = "UTF-8";
            }
            // 对于原子服务配置的发送字符集和用户请求的字符集是否一致
            if(!atomReqCharset.equalsIgnoreCase(reqCharset)) {
                setAtomReqStatus("3");
                responseMsg = "Please check your character set encoding. Use "+ atomReqCharset;
                throw new Exception(responseMsg);
            }
            // http请求
            if("1".equals(adapterType)) {
                responseMsg = httpProcess(responseMsg, httpType, queryString, atomUrl, atomRespCharset, atomOverTime, atomContentType,
                        contentType, requestMsg, atomReqCharset, postChangedGet, map);
                // webservice请求
            }else if("2".equals(adapterType)) {
                responseMsg = soapProcess(responseMsg, atomUrl, requestMsg, atomReqCharset, atomRespCharset, atomOverTime, map);
                if(isEmpty(responseMsg)) {
                    setAtomReqStatus("3");
                    responseMsg = "The webservice interface can't get any response.";
                }

            }else {
                setAtomReqStatus("3");
                responseMsg = "The adaptation type of this atomservice is not " + adapterTypeName +
                        "! Please check the service configuration.";
                throw new Exception(responseMsg);
            }

        }catch (Throwable e) {
            if("DCSB".equals(responseMsg)) {
                setAtomReqStatus("3");
                responseMsg = "原子服务的服务器异常，请联系管理员!";
            }
        }

        atomResponseTime = System.currentTimeMillis();

        // 超出原子服务设置的超时时间
        long dealTime = atomOverTime.longValue();
        if(atomResponseTime >= (atomRequestTime + dealTime)) {
            setAtomReqStatus("3");
        }
        // 判断网络异常，端口不通, 95服务器好像不会执行这个
        if(responseMsg.contains("Connection timed out")) {
            setAtomReqStatus("3");
            responseMsg = "Connection timed out: please check the network connection.";
        }

        // 保存原子服务日志
        Timestamp reqTime = new Timestamp(atomRequestTime);
        Timestamp respTime = new Timestamp(atomResponseTime);

        JSONObject esbAtomlogJson = new JSONObject();
        esbAtomlogJson.put("domainname", domainName);
        esbAtomlogJson.put("appname", appNmae);
        esbAtomlogJson.put("reqid", reqDcsbId);
        esbAtomlogJson.put("atomexpid", atomServiceId);
        esbAtomlogJson.put("status", getAtomReqStatus());
        esbAtomlogJson.put("request_time", reqTime);
        esbAtomlogJson.put("response_time", respTime);
        try{
            mqProducerService.send(MqConstants.Tags.ESB_ATOMLOG, esbAtomlogJson.toJSONString());
        }catch (Exception e) {
            log.info(e.getMessage());
            log.info("时间：" + reqTime + "保存原子服务日志到mq失败");
        }

        JSONObject esbAtomlogReqJson = new JSONObject();
        esbAtomlogReqJson.put("reqid", reqDcsbId);
        esbAtomlogReqJson.put("atomexpid", atomServiceId);
        esbAtomlogReqJson.put("insoapmsg", requestMsg);
        esbAtomlogReqJson.put("outsoapmsg", responseMsg);
        esbAtomlogReqJson.put("request_time", reqTime);
        esbAtomlogReqJson.put("response_time", respTime);

        if(esbAtomlogReqJson.toJSONString().length() > 1024*1024*4) {
            esbAtomlogReqJson.put("outsoapmsg", "");
            Integer num = 1;
            String response = responseMsg;
            while(response.length() > 1023*1024*4) {
                String outsoapmsg = response.substring(0, 1023*1024*4);
                JSONObject esbAtomlogReqSplitJson = new JSONObject();
                esbAtomlogReqSplitJson.put("reqid", "atom"+reqDcsbId);
                esbAtomlogReqSplitJson.put("outsoapmsg", outsoapmsg);
                esbAtomlogReqSplitJson.put("num", num);
                try{
                    mqProducerService.send(MqConstants.Tags.ESB_ATOMLOG_REQ_SPLIT, esbAtomlogReqSplitJson.toJSONString());
                }catch (Exception e) {
                    log.info(e.getMessage());
                    log.info("时间：" + reqTime + "保存原子服务拆分报文到mq失败：流水号" + reqDcsbId + ",num = " + num);
                }
                response = response.substring(1023*1024*4);
                num++;
            }
            JSONObject esbAtomlogReqSplitJson = new JSONObject();
            esbAtomlogReqSplitJson.put("reqid", "atom"+reqDcsbId);
            esbAtomlogReqSplitJson.put("outsoapmsg", response);
            esbAtomlogReqSplitJson.put("num", num);
            esbAtomlogReqSplitJson.put("size", num);
            try{
                mqProducerService.send(MqConstants.Tags.ESB_ATOMLOG_REQ_SPLIT, esbAtomlogReqSplitJson.toJSONString());
            }catch (Exception e) {
                log.info(e.getMessage());
                log.info("时间：" + reqTime + "保存原子服务拆分报文到mq失败：流水号" + reqDcsbId + ",num = " + num);
            }
        }
        try{
            mqProducerService.send(MqConstants.Tags.ESB_ATOMLOG_REQ, esbAtomlogReqJson.toJSONString());
        }catch (Exception e) {
            log.info(e.getMessage());
            log.info("时间：" + reqTime + "保存原子服务报文日志到mq失败");
        }

        return responseMsg;
    }

    public String getAtomReqStatus() {
        return atomReqStatus;
    }
    public void setAtomReqStatus(String atomReqStatus) {
        this.atomReqStatus = atomReqStatus;
    }

    /**
     * http请求处理
     * @param responseMsg
     * @param httpType
     * @param queryString
     * @param atomUrl
     * @param atomRespCharset
     * @param atomOverTime
     * @param atomContentType
     * @param contentType
     * @param requestMsg
     * @param atomReqCharset
     * @return
     */
    private String httpProcess(String responseMsg, String httpType, String queryString, String atomUrl, String atomRespCharset,
                               Integer atomOverTime, String atomContentType, String contentType, String requestMsg,
                               String atomReqCharset, String postChangedGet, HashMap<String, String> map) {
        // 分GET和POST情况讨论
        if("GET".equalsIgnoreCase(httpType)){
            if("1".equals(postChangedGet)) {
                atomContentType = getAtomContentType(atomContentType);
                if(atomContentType.equalsIgnoreCase(contentType)){
                    if("multipart/form-data".equalsIgnoreCase(atomContentType) || "application/x-www-form-urlencoded".equalsIgnoreCase(atomContentType)) {
                        if(!isEmpty(requestMsg)) {
                            atomUrl = atomUrl + "?"+ requestMsg;
                        }
                        setAtomReqStatus("0");
                        responseMsg = HttpReqUtil.get(atomUrl, atomRespCharset, atomOverTime, map);
                    }else {
                        setAtomReqStatus("3");
                        responseMsg = "The get request can't be registered into a post method request of the header is " + contentType + ".";
                    }
                }else {
                    setAtomReqStatus("3");
                    responseMsg = "Request method error! Please send a post request of Content-Type: "+ atomContentType +".";
                }
            }else {
                if(isEmpty(queryString)) {
                    //get请求地址不变
                }else {
                    atomUrl = atomUrl + "?" +queryString;
                }
                setAtomReqStatus("0");
                responseMsg = HttpReqUtil.get(atomUrl, atomRespCharset, atomOverTime, map);
            }
        }else if("POST".equalsIgnoreCase(httpType)) {
            if(!"1".equals(postChangedGet)) {
                // 处理post请求可能存在 地址后直接加参数的情况
                if(!contentType.equals(queryString)) {
                    atomUrl = atomUrl + "?" +queryString;
                }
                // 请求头类型数字与中文转换
                atomContentType = getAtomContentType(atomContentType);
                // 对比原子服务配置和用户请求时发起的请求头类型
                if(!"".equals(contentType) && !atomContentType.equalsIgnoreCase(contentType)){
                    setAtomReqStatus("3");
                    responseMsg = "The request contentType: "+ contentType +" is incorrect! Choose "+
                            atomContentType +" to consumer service.";
                    return responseMsg;
                }
                // 处理post请求的各种请求头类型
                if("multipart/form-data".equals(contentType) || "".equals(contentType)) {
                    // 截取去除请求参数中的url后的参数
                    if(!"multipart/form-data".equalsIgnoreCase(queryString)) {
                        if(queryString.equals(requestMsg)) {
                            requestMsg = "";
                        }else {
                            requestMsg = requestMsg.substring(queryString.length()+1);
                        }
                    }
                    String[] msg = {};
                    if(!isEmpty(requestMsg)) {
                        msg = requestMsg.split("&");
                    }
                    Part[] parts= new Part[msg.length];
                    String key = "";
                    String value = "";
                    for(int i=0; i<msg.length; i++) {
                        key = msg[i].substring(0, msg[i].indexOf("="));
                        value = msg[i].substring(msg[i].indexOf("=")+1);
                        parts[i] = new StringPart(key, value, atomReqCharset);
                    }
                    setAtomReqStatus("0");
                    responseMsg = HttpReqUtil.postMultipart(atomUrl, parts, atomReqCharset, atomRespCharset, atomOverTime, map);
                }else if("application/json".equals(contentType)) {
                    try {
                        setAtomReqStatus("0");
                        responseMsg = HttpReqUtil.postJson(atomUrl, requestMsg, atomReqCharset, atomRespCharset, atomOverTime, map);
                    }catch (Exception e) {
                        setAtomReqStatus("3");
                        responseMsg = "Request error. " + e.getMessage();
                    }
                }else if("application/x-www-form-urlencoded".equals(contentType)) {
                    // 截取去除请求参数中的url后的参数
                    if(!"application/x-www-form-urlencoded".equalsIgnoreCase(queryString)) {
                        if(queryString.equals(requestMsg)) {
                            requestMsg = "";
                        }else {
                            requestMsg = requestMsg.substring(queryString.length()+1);
                        }
                    }
                    String[] msg = {};
                    if(!isEmpty(requestMsg)) {
                        msg = requestMsg.split("&");
                    }
                    NameValuePair[] param = new NameValuePair[msg.length];
                    String key = "";
                    String value = "";
                    for(int i=0; i<msg.length; i++) {
                        key = msg[i].substring(0, msg[i].indexOf("="));
                        value = msg[i].substring(msg[i].indexOf("=")+1);
                        param[i] = new NameValuePair(key, value);
                    }
                    setAtomReqStatus("0");
                    responseMsg = HttpReqUtil.postUrlencoded(atomUrl, param, atomReqCharset, atomRespCharset, atomOverTime, map);
                }else if("text/xml".equals(contentType)) {

                    setAtomReqStatus("3");
                    responseMsg = "原子服务请求头类型为:"+contentType;
                }else if("application/xml".equals(contentType)) {

                    setAtomReqStatus("3");
                    responseMsg = "原子服务请求头类型为:"+contentType;
                }else {

                    setAtomReqStatus("3");
                    responseMsg = "暂不支持此类请求头类型:"+contentType;
                }
            }else {

                setAtomReqStatus("3");
                responseMsg = "Post interface can't be registered as get";
            }

        }else {

            setAtomReqStatus("3");
            responseMsg = "This request method is not get or post. Error request!";
        }
        return responseMsg;
    }

    private String soapProcess(String responseMsg, String atomUrl, String requestMsg, String atomReqCharset,
                               String atomRespCharset, Integer atomOverTime, HashMap<String, String> map) {
        byte[] b = null;
        try {
            b = requestMsg.getBytes(atomReqCharset);
        }catch (Exception e) {
            setAtomReqStatus("3");
            responseMsg = "Atomservice can't get the requestmsg." + e.getMessage();
            return responseMsg;
        }
        StringBuffer response = new StringBuffer();
        URL url = null;
        HttpURLConnection httpConn = null;
        OutputStream out = null;
        BufferedReader in = null;
        InputStreamReader isr = null;
        try {
            // String key = soapUrl;
            url = new URL(atomUrl);
            // 打开连接
            httpConn = (HttpURLConnection) url.openConnection();
            httpConn.setRequestProperty("Content-Length", String.valueOf(b.length));
            httpConn.setRequestProperty("Content-Type", "text/xml;charset=" + atomReqCharset);
            httpConn.setRequestProperty("SOAPAction", "\"\"");

            if(map.size() != 0){
                for(Map.Entry<String, String> entry : map.entrySet()) {
                    httpConn.setRequestProperty(entry.getKey(), entry.getValue());
                }
            }

            httpConn.setRequestMethod("POST");
            httpConn.setDoOutput(true);
            httpConn.setDoInput(true);
            // 设置接口超时时间
            httpConn.setReadTimeout(atomOverTime);
            httpConn.setConnectTimeout(atomOverTime);

            // 获取返回流
            out = httpConn.getOutputStream();
            out.write(b);
            out.close();
            out = null;

            // Read the response and write it to standard out.
            isr = new InputStreamReader(httpConn.getInputStream(), atomRespCharset);
            in = new BufferedReader(isr);

            // 拼接返回串
            String inputLine = null;
            while((inputLine = in.readLine()) != null) {
                response.append(inputLine);
            }

            in.close();
            in = null;
            isr.close();
            isr = null;

            responseMsg =  response.toString();
            // 转义操作
            responseMsg = StringEscapeUtils.unescapeHtml4(responseMsg);
            setAtomReqStatus("0");
        } catch (SocketTimeoutException ste) {
            setAtomReqStatus("3");
            responseMsg = "call http server timeout."+ ste.getMessage();
        } catch (Exception ex) {
            setAtomReqStatus("3");
            responseMsg = "Exception error." + ex.getMessage();
        } finally {
            if(httpConn != null) {
                httpConn.disconnect();
            }
            if(out != null) {
                try {
                    out.close();
                } catch (IOException e) {

                }
            }
            if(in != null) {
                try {
                    in.close();
                } catch (IOException e) {

                }
            }
            if(isr != null){
                try {
                    isr.close();
                } catch (IOException e) {

                }
            }
        }
        return responseMsg;
    }

    /**
     * 把数字转换成对应的解释
     * @param atomContentType 请求头类型
     * @return
     */
    private String getAtomContentType(String atomContentType) {
        if("1".equals(atomContentType)) {
            atomContentType = "multipart/form-data";
        }else if("2".equals(atomContentType)) {
            atomContentType = "application/json";
        }else if("3".equals(atomContentType)) {
            atomContentType = "application/x-www-form-urlencoded";
        }else if("4".equals(atomContentType)) {
            atomContentType = "text/xml";
        }else if("5".equals(atomContentType)) {
            atomContentType = "application/xml";
        }
        return atomContentType;
    }

}

